﻿namespace Vibstudio.NetConfigurator.Engine.Composition
{
    #region Namespaces

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Xml.Linq;

    using Vibstudio.NetConfigurator.Common;
    using Vibstudio.NetConfigurator.Exceptions;
    using Vibstudio.NetConfigurator.Model;

    #endregion

    [Obfuscation(Exclude = true, ApplyToMembers = false)]
    internal class ApplicationComposer : IComposition
    {
        #region Fields

        private IList<string> _lostCompositions;

        #endregion

        #region Public Methods and Operators

        public bool Compose(NetConfig netConfig)
        {
            _lostCompositions = new List<string>();

            string configurationFileReferencePath = netConfig.ConfigurationFileReferencePath;
            string configurationsFilePath = ConfigurationHelper.ConfigurationsFilePath;

            XDocument referenceFile = XDocument.Load(configurationFileReferencePath);
            XDocument configurationsFile = XDocument.Load(configurationsFilePath);

            bool composed = PerformComposition(referenceFile,
                                               configurationsFile,
                                               netConfig.ConfigurationID,
                                               netConfig.ConfigurationSettings);

            if (composed)
            {
                configurationsFile.Save(configurationsFilePath);
            }

            if (_lostCompositions.Count > 0)
            {
                throw new CompositionPartiallyPerformedException(_lostCompositions);
            }

            return composed;
        }

        public void Reset()
        {
        }

        #endregion

        #region Methods

        private bool PerformComposition(XDocument referenceFile,
                                        XDocument targetFile,
                                        string configurationID,
                                        IEnumerable<ConfigurationSetting> settings)
        {
            try
            {
                foreach (ConfigurationSetting setting in settings)
                {
                    XElement referenceElement =
                        referenceFile
                            .Descendants("applicationSettings")
                            .Descendants("setting")
                            .FirstOrDefault(e => (string)e.Attribute("name") == setting.Key);

                    if (referenceElement == null)
                    {
                        _lostCompositions.Add(setting.Key);
                    }
                    else
                    {
                        XElement targetElement =
                            targetFile
                                .Descendants("configuration")
                                .Elements("name")
                                .Where(e => (string)e.Attribute("id") == configurationID)
                                .Elements("add")
                                .FirstOrDefault(e => (string)e.Attribute("key") == setting.Key);

                        if (targetElement != null)
                        {
                            XAttribute targetValueAttribute = targetElement.Attribute("value");
                            if (targetValueAttribute != null)
                            {
                                targetValueAttribute.SetValue(referenceElement.Value);
                            }
                        }
                    }
                }

                return true;
            }
            catch (Exception exception)
            {
                Trace.WriteLine(string.Format("Exception: {0}", exception), "ApplicationComposer.PerformComposition");
                throw;
            }
        }

        #endregion
    }
}
